package org.imraniqbal.engine;

/**
 * @author  imran
 */
public abstract class Player extends Mappable {

	private static final long serialVersionUID = 8439534138826600432L;
	/**
	 * @uml.property  name="direction"
	 * @uml.associationEnd  
	 */
	private Direction direction;
	/**
	 * @uml.property  name="numOflives"
	 */
	private int numOflives;
	private int oldX;
	private int oldY;
	/**
	 * General purpose constructor for Player. Sets direction to Direction.LEFT and numOfLives to 1.
	 * @param x Desired X position of Player
	 * @param y Desired Y position of Player
	 * @author imran
	 */
	public Player(int x, int y){
		super(x, y);
		oldX = x;
		oldY = y;
		setDirection(Direction.LEFT);
		setNumOflives(1);
	}
	/**
	 * A more specific constructor for Player. Allows direction and numOfLives to be set to desired values.
	 * @param x Desired X position of Player
	 * @param y Desired Y position of Player
	 * @param direction Desired direction of player ( UP, DOWN, LEFT, RIGHT )
	 * @param numOflives desired number of lives
	 * @author imran
	 */
	public Player(int x, int y, Direction direction, int numOflives) {
		super(x, y);
		oldX = x;
		oldY = y;
		setDirection(direction);
		setNumOflives(numOflives);
		
	}
	
	/**
	 * @return   Current direction of Player
	 * @author  imran
	 * @uml.property  name="direction"
	 */
	public Direction getDirection() {
		return direction;
	}
	
	/**
	 * Change the current direction of Player
	 * @param direction   Either UP, DOWN, LEFT or RIGHT
	 * @author  imran
	 * @uml.property  name="direction"
	 */
	public void setDirection(Direction direction) {
		this.direction = direction;
	}
	
	/**
	 * @return   Number of lives of the Player
	 * @author  imran
	 * @uml.property  name="numOflives"
	 */
	public int getNumOflives() {
		return numOflives;
	}
	public int getOldX() {
		return oldX;
	}
	public int getOldY() {
		return oldY;
	}

	/**
	 * Change the current number of lives of the Player
	 * @param numOflives   number of lives desired.
	 * @author  imran
	 * @uml.property  name="numOflives"
	 */
	public void setNumOflives(int numOflives) {
		this.numOflives = numOflives;
	}
	
	/**
	 * Moves the Player in its current direction.
	 * @param Map current map the Player is on
	 * @return true if the move was successful, false otherwise.
	 */
	public boolean updateLocation(Map map){
		switch(this.direction){
			case UP:
				return move(this.getX(), this.getY() - 1, map);
			case LEFT:
				return move(this.getX() - 1, this.getY(), map);
			case DOWN:
				return move(this.getX(), this.getY() + 1, map);
			case RIGHT:
				return move(this.getX() + 1, this.getY(), map);
		}
		//Random return to pacify Eclipse
		return false;
	}
	
	private boolean move(int x, int y, Map map){
		if(!map.isWall(x, y)){
			map.removeMappable(this);
			this.oldX = getX();
			this.oldY = getY();
			this.setX(x);
			this.setY(y);
			return map.addMappable(this);
		}
		return false;
	}

	public abstract void spawn(Map map);
	/**
	 * Kills the player, by removing itself from the map and decrementing remaining lives
	 * if lives == 0. player does not respawn
	 * @param map
	 */
	public void die(Map map){
		setNumOflives(this.getNumOflives() - 1);
		map.removeMappable(this);
		if( this.getNumOflives() != 0 ){
			this.spawn(map);
		}
	}
	
	/**
	 * Player's are only equal when their position, direction and lives are the same
	 * @author imran
	 */
	@Override
	public boolean equals(Object obj) {
		if(!(obj instanceof Player)){ return false; }
		return(((Player)obj).getX() == this.getX() && ((Player)obj).getY() == this.getY() &&
				((Player)obj).getOldX() == this.getOldX() && ((Player)obj).getOldY() == this.getOldY() &&
				((Player)obj).getDirection() == this.getDirection() &&
				((Player)obj).getNumOflives() == this.getNumOflives());
	}
}
